/*
JSNES, based on Jamie Sanders' vNES
Copyright (C) 2010 Ben Firshman

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

JSNES.DummyUI = function(nes) {
    this.nes = nes;
    this.enable = function() {};
    this.updateStatus = function() {};
    this.writeAudio = function() {};
    this.writeFrame = function() {};
};

if (typeof jQuery !== 'undefined') {
    (function($) {
        $.fn.JSNESUI = function(roms) {
            var parent = this;
            var UI = function(nes) {
                var self = this;
                self.nes = nes;
                
                /*
                 * Create UI
                 */
                self.root = $('<div></div>');
                self.screen = $('<canvas class="nes-screen" width="256" height="240" style="width:100%"></canvas>').appendTo(self.root);
                
                if (!self.screen[0].getContext) {
                    parent.html("Your browser doesn't support the <code>&lt;canvas&gt;</code> tag. Try Google Chrome, Safari, Opera or Firefox!");
                    return;
                }
                
                self.romContainer = $('<div class="nes-roms"></div>').appendTo(self.root);
                self.romSelect = $('<select></select>').appendTo(self.romContainer);
                
                self.controls = $('<div class="nes-controls"></div>').appendTo(self.root);
                self.buttons = {
                    pause: $('<input type="button" value="pause" class="nes-pause" disabled="disabled">').appendTo(self.controls),
                    restart: $('<input type="button" value="restart" class="nes-restart" disabled="disabled">').appendTo(self.controls),
                    sound: $('<input type="button" value="enable sound" class="nes-enablesound">').appendTo(self.controls),
                    //zoom: $('<input type="button" value="big" class="nes-zoom">').appendTo(self.controls)
                };
                
                //$('<input type="button" value="bbs" class="nes-bbs" onclick="location.href=\'http:\/\/xueduanyang.xyz\/bbs\/\'">').appendTo(self.controls)
                
                self.status = $('<p class="nes-status">Booting up...</p>').appendTo(self.root);
                self.root.appendTo(parent);
                
                /*
                 * ROM loading
                 */
                self.romSelect.change(function() {
                    self.loadROM();
                });
                
                /*
                 * Buttons
                 */
                self.buttons.pause.click(function() {
                    if (self.nes.isRunning) {
                        self.nes.stop();
                        self.updateStatus("Paused");
                        self.buttons.pause.attr("value", "resume");
                    }
                    else {
                        self.nes.start();
                        self.buttons.pause.attr("value", "pause");
                    }
                });
        
                self.buttons.restart.click(function() {
                    self.nes.reloadRom();
                    self.nes.start();
                });
        
                self.buttons.sound.click(function() {
                    if (self.nes.opts.emulateSound) {
                        self.nes.opts.emulateSound = false;
                        self.buttons.sound.attr("value", "enable sound");
                        
                        
                        
                        
                    }
                    else {
                        self.nes.opts.emulateSound = true;
                        self.buttons.sound.attr("value", "disable sound");
                        
                        var source = self.audio.createBufferSource();
                        source.connect(self.audio.destination); // Output to sound
    					source.start();
                    }
                });
        
                self.zoomed = false;

                
                $('.nes-screen').css({
        			'max-height': document.documentElement.clientHeight,
        		})
                $(window).bind('resize', function(){
                	$('.nes-screen').css({
            			'max-height': document.documentElement.clientHeight,
            		})
                })
                if(/(IPHONE|IPAD|ANDROID)/i.test(navigator.userAgent)){
                	$('#pc-controlls').hide();
                }else{
                	//$('.nes-zoom').hide();
                	$('#mobile-controlls').hide();
                	//$('.shang').removeClass('anim_m').addClass('pc');
                }
                
                /*self.buttons.zoom.click(function() {
                    if (self.zoomed) {
//                        self.screen.animate({
//                            width: '256px',
//                            height: '240px'
//                        });
                        self.buttons.zoom.attr("value", "big");
                        $('body').removeClass('big');
                        $('.nes-screen').css({
                			height: 'auto',
                			width: '100%',
                			'max-width': document.documentElement.clientWidth,
                			'max-height': document.documentElement.clientHeight
                		})
                		$('body').css({
                			width: 'auto'
                		})
                        
                        
                        self.zoomed = false;
                    }
                    else {
//                        self.screen.animate({
//                            width: '512px',
//                            height: '480px'
//                        });
                    	$('body').addClass('big');
                    	if(document.documentElement.clientHeight < screen.availHeight){
                    		$('body').css({
                    			width: document.documentElement.clientHeight,
                    		})
                    	}
                    	$('.big .nes-screen').css({
                			height: document.documentElement.clientWidth,
                			width: 'auto'
                		})
                        self.buttons.zoom.attr("value", "small");
                        self.zoomed = true;
                    }
                });*/
                
                /*
                 * Lightgun experiments with mouse
                 * (Requires jquery.dimensions.js)
                 */
                if ($.offset) {
                    self.screen.mousedown(function(e) {
                        if (self.nes.mmap) {
                            self.nes.mmap.mousePressed = true;
                            // FIXME: does not take into account zoom
                            self.nes.mmap.mouseX = e.pageX - self.screen.offset().left;
                            self.nes.mmap.mouseY = e.pageY - self.screen.offset().top;
                        }
                    }).mouseup(function() {
                        setTimeout(function() {
                            if (self.nes.mmap) {
                                self.nes.mmap.mousePressed = false;
                                self.nes.mmap.mouseX = 0;
                                self.nes.mmap.mouseY = 0;
                            }
                        }, 500);
                    });
                }
            
                if (typeof roms != 'undefined') {
                    self.setRoms(roms);
                }
            
                /*
                 * Canvas
                 */
                self.canvasContext = self.screen[0].getContext('2d');
                
                if (!self.canvasContext.getImageData) {
                    parent.html("Your browser doesn't support writing pixels directly to the <code>&lt;canvas&gt;</code> tag. Try the latest versions of Google Chrome, Safari, Opera or Firefox!");
                    return;
                }
                
                self.canvasImageData = self.canvasContext.getImageData(0, 0, 256, 240);
                self.resetCanvas();
            
                /*
                 * Keyboard
                 */
                $(document).
                    bind('keydown', function(evt) {
                        self.nes.keyboard.keyDown(evt); 
                    }).
                    bind('keyup', function(evt) {
                        self.nes.keyboard.keyUp(evt); 
                    }).
                    bind('keypress', function(evt) {
                        self.nes.keyboard.keyPress(evt);
                    });
            
                
                
                $('#controls-direction').bind('touchstart', function(e){
                	handleDirection(e);
                	e.preventDefault();
                })
                $('#controls-direction').bind('gesturestart', function(e){
                	handleDirection(e);
                	e.preventDefault();
                })
                $('#controls-direction').bind('touchmove', function(e){
                	handleDirection(e);
                })
                function handleDirection(e){
                	var myLocation = e.originalEvent.changedTouches[0];
                	var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);
                	if($(realTarget).hasClass('leftup')){
                		$('#controls-direction .left').addClass('active');
                		$('#controls-direction .up').addClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyDown({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('up')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').addClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('rightup')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').addClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').addClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('left')){
                		$('#controls-direction .left').addClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyDown({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('center')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('right')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').removeClass('active');
                		$('#controls-direction .right').addClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('leftdown')){
                		$('#controls-direction .left').addClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').addClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyDown({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('down')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').addClass('active');
                		$('#controls-direction .right').removeClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 40
                    	});
                	}else if($(realTarget).hasClass('rightdown')){
                		$('#controls-direction .left').removeClass('active');
                		$('#controls-direction .up').removeClass('active');
                		$('#controls-direction .down').addClass('active');
                		$('#controls-direction .right').addClass('active');
                		self.nes.keyboard.keyUp({
                    		keyCode: 37
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 38
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 39
                    	});
                		self.nes.keyboard.keyDown({
                    		keyCode: 40
                    	});
                	}
                	
                }
                $('#controls-direction').bind('touchend', function(e){
                	$('#controls-direction .left').removeClass('active');
            		$('#controls-direction .up').removeClass('active');
            		$('#controls-direction .down').removeClass('active');
            		$('#controls-direction .right').removeClass('active');
                	self.nes.keyboard.keyUp({
                		keyCode: 37
                	});
            		self.nes.keyboard.keyUp({
                		keyCode: 38
                	});
            		self.nes.keyboard.keyUp({
                		keyCode: 39
                	});
            		self.nes.keyboard.keyUp({
                		keyCode: 40
                	});
                });
                
                $('#joystick_btn_up').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 38
                	});
                	e.preventDefault();
                	
                });
                $('#joystick_btn_up').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 38
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_down').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 40
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_down').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 40
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_left').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 37
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_left').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 37
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_right').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 39
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_right').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 39
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_A').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 88
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_A').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 88
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_B').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 89
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_B').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 89
                	});
                	e.preventDefault();
                });
                $('#joystick_btn_select').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 17
                	});
                	$('#joystick_btn_select').addClass('active');
                	e.preventDefault();
                });
                $('#joystick_btn_select').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 17
                	});
                	$('#joystick_btn_select').removeClass('active');
                	e.preventDefault();
                });
                $('#joystick_btn_start').bind('touchstart', function(e){
                	self.nes.keyboard.keyDown({
                		keyCode: 13
                	});
                	$('#joystick_btn_start').addClass('active');
                	e.preventDefault();
                });
                $('#joystick_btn_start').bind('touchend', function(e){
                	self.nes.keyboard.keyUp({
                		keyCode: 13
                	});
                	$('#joystick_btn_start').removeClass('active');
                	e.preventDefault();
                });
                
                $('#controls-fire').bind('touchstart', function(e){
                	handleFire(e);
                	e.preventDefault();
                });
                $('#controls-fire').bind('gesturestart', function(e){
                	handleFire(e, true);
                	e.preventDefault();
                });
                $('#controls-fire').bind('touchmove', function(e){
                	handleFire(e);
                	e.preventDefault();
                });
                $('#controls-fire').bind('touchend', function(e){
                	clearInterval(self.interval);
                	$('#controls-fire .a').removeClass('active');
            		$('#controls-fire .b').removeClass('active');
                	self.nes.keyboard.keyUp({
                		keyCode: 88
                	});
            		self.nes.keyboard.keyUp({
                		keyCode: 90
                	});
                	e.preventDefault();
                });
                $('#controls-turbofire').bind('touchstart', function(e){
                	handleFire(e, true);
                	e.preventDefault();
                });
                $('#controls-turbofire').bind('gesturestart', function(e){
                	handleFire(e, true);
                	e.preventDefault();
                });
                $('#controls-turbofire').bind('touchmove', function(e){
                	handleFire(e, true);
                	e.preventDefault();
                });
                $('#controls-turbofire').bind('touchend', function(e){
                	clearInterval(self.interval);
                	$('#controls-turbofire .a').removeClass('active');
            		$('#controls-turbofire .b').removeClass('active');
                	self.nes.keyboard.keyUp({
                		keyCode: 88
                	});
            		self.nes.keyboard.keyUp({
                		keyCode: 90
                	});
                	e.preventDefault();
                });
                function handleFire(e, turbo){
                	var parent = $('#controls-fire');
                	if(turbo){
                		parent = $('#controls-turbofire');
                	}
                	var myLocation = e.originalEvent.changedTouches[0];
                	var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);
                	if($(realTarget).hasClass('a')){
                		$('.a', parent).addClass('active');
                		$('.b', parent).removeClass('active');
                		clearInterval(self.interval);
                		if(turbo){
                			self.nes.keyboard.keyDown({
                        		keyCode: 'AA'
                        	});
                			self.nes.keyboard.keyUp({
                        		keyCode: 90
                        	});
                    		self.interval = setInterval(function(){
                    			self.nes.keyboard.keyDown({
                            		keyCode: 'AA'
                            	});
                    		}, 50);
                		}else{
                			self.nes.keyboard.keyDown({
                        		keyCode: 88
                        	});
                    		self.nes.keyboard.keyUp({
                        		keyCode: 90
                        	});
                		}
                	}else if($(realTarget).hasClass('b')){
                		$('.a', parent).removeClass('active');
                		$('.b', parent).addClass('active');
                		clearInterval(self.interval);
                		if(turbo){
                			self.nes.keyboard.keyUp({
                        		keyCode: 88
                        	});
                    		self.nes.keyboard.keyDown({
                        		keyCode: 'BB'
                        	});
                			self.interval = setInterval(function(){
                    			self.nes.keyboard.keyDown({
                            		keyCode: 'BB'
                            	});
                    		}, 50);	
                		}else{
                			self.nes.keyboard.keyUp({
                        		keyCode: 88
                        	});
                    		self.nes.keyboard.keyDown({
                        		keyCode: 90
                        	});
                		}
                	}else if($(realTarget).hasClass('c')){
                		$('.a', parent).addClass('active');
                		$('.b', parent).addClass('active');
                		clearInterval(self.interval);
                		if(turbo){
                			self.nes.keyboard.keyDown({
                        		keyCode: 'AA'
                        	});
                			self.nes.keyboard.keyDown({
                        		keyCode: 'BB'
                        	});
                			self.interval = setInterval(function(){
                    			self.nes.keyboard.keyDown({
                            		keyCode: 'AA'
                            	});
                    			self.nes.keyboard.keyDown({
                            		keyCode: 'BB'
                            	});
                    		}, 50);	
                		}else{
                			self.nes.keyboard.keyDown({
                        		keyCode: 88
                        	});
                    		self.nes.keyboard.keyDown({
                        		keyCode: 90
                        	});
                		}
                	}else{
                		clearInterval(self.interval);
                    	$('.a', parent).removeClass('active');
                		$('.b', parent).removeClass('active');
                    	self.nes.keyboard.keyUp({
                    		keyCode: 88
                    	});
                		self.nes.keyboard.keyUp({
                    		keyCode: 90
                    	});
                		
                	}
                }
                
                /*
                 * Sound
                 */
             // Workaround prefixed naming used in Safary 8-9
//                self.dynamicaudio = new DynamicAudio({
//                    swf: nes.opts.swfPath+'dynamicaudio.swf'
//                });
                window.AudioContext = window.webkitAudioContext || window.AudioContext;
                try {
                    self.audio = new AudioContext();
                } catch(e) {
                    // lets fallback to Flash (for Internet Explorer 8-11)
                	console.error(e);
                    self.dynamicaudio = new DynamicAudio({
                        swf: nes.opts.swfPath+'dynamicaudio.swf'
                    });
                }
            };
        
            UI.prototype = {    
                loadROM: function(name) {
                    var self = this;
                    self.updateStatus("Downloading...");
                    $.ajax({
                        url: (name || self.romSelect.val()),
                        xhr: function() {
                            var xhr = $.ajaxSettings.xhr();
                            if (typeof xhr.overrideMimeType !== 'undefined') {
                                // Download as binary
                                xhr.overrideMimeType('text/plain; charset=x-user-defined');
                            }
                            self.xhr = xhr;
                            return xhr;
                        },
                        complete: function(xhr, status) {
                            var i, data;
                            if (JSNES.Utils.isIE()) {
                                var charCodes = JSNESBinaryToArray(
                                    xhr.responseBody
                                ).toArray();
                                data = String.fromCharCode.apply(
                                    undefined, 
                                    charCodes
                                );
                            }
                            else {
                                data = xhr.responseText;
                            }
                            self.nes.loadRom(data);
                            self.nes.start();
                            self.enable();
                        }
                    });
                },
                
                resetCanvas: function() {
                    this.canvasContext.fillStyle = 'black';
                    // set alpha to opaque
                    this.canvasContext.fillRect(0, 0, 256, 240);

                    // Set alpha
                    for (var i = 3; i < this.canvasImageData.data.length-3; i += 4) {
                        this.canvasImageData.data[i] = 0xFF;
                    }
                },
                
                /*
                *
                * nes.ui.screenshot() --> return <img> element :)
                */
                screenshot: function() {
                    var data = this.screen[0].toDataURL("image/png"),
                        img = new Image();
                    img.src = data;
                    return img;
                },
                
                /*
                 * Enable and reset UI elements
                 */
                enable: function() {
                    this.buttons.pause.attr("disabled", null);
                    if (this.nes.isRunning) {
                        this.buttons.pause.attr("value", "pause");
                    }
                    else {
                        this.buttons.pause.attr("value", "resume");
                    }
                    this.buttons.restart.attr("disabled", null);
                    if (this.nes.opts.emulateSound) {
                        this.buttons.sound.attr("value", "disable sound");
                    }
                    else {
                        this.buttons.sound.attr("value", "enable sound");
                        
                        
                    }
                },
            
                updateStatus: function(s) {
                    this.status.text(s);
                },
        
                setRoms: function(roms) {
                    this.romSelect.children().remove();
                    $("<option>切换游戏...</option>").appendTo(this.romSelect);
                    for (var groupName in roms) {
                        if (roms.hasOwnProperty(groupName)) {
                            var optgroup = $('<optgroup></optgroup>').
                                attr("label", groupName);
                            for (var i = 0; i < roms[groupName].length; i++) {
                                $('<option>'+roms[groupName][i][0]+'</option>')
                                    .attr("value", roms[groupName][i][1])
                                    .appendTo(optgroup);
                            }
                            this.romSelect.append(optgroup);
                        }
                    }
                },
            
                writeAudio: function(samples) {
                    //return this.dynamicaudio.writeInt(samples);
                	// Use fallback if available and return early
					if (this.dynamicaudio) {
						return this.dynamicaudio.writeInt(samples);
					}
					// Create output buffer (planar buffer format)
					var buffer = this.audio.createBuffer(2, samples.length, this.audio.sampleRate);
					var channelLeft = buffer.getChannelData(0);
					var channelRight = buffer.getChannelData(1);
					// Convert from interleaved buffer format to planar buffer
					// by writing right into appropriate channel buffers
					var j = 0;
					for (var i = 0; i < samples.length; i += 2) {
						channelLeft[j] = this.intToFloatSample(samples[i]);
						channelRight[j] = this.intToFloatSample(samples[i + 1]);
						j++;
					}
					// Create sound source and play it
					var source = this.audio.createBufferSource();
					source.buffer = buffer;
					source.connect(this.audio.destination); // Output to sound
															// card
					source.start();
                },
             // Local helper function to convert Int output to Float
                               // TODO: remove intToFloat and revise papu.js -> sample()
                               //       to return AudioBuffer/Float32Array output used in HTML5 WebAudio API
               intToFloatSample: function(value) {
                   return value / 32767; // from -32767..32768 to -1..1 range
               },
            
                writeFrame: function(buffer, prevBuffer) {
                    var imageData = this.canvasImageData.data;
                    var pixel, i, j;

                    for (i=0; i<256*240; i++) {
                        pixel = buffer[i];

                        if (pixel != prevBuffer[i]) {
                            j = i*4;
                            imageData[j] = pixel & 0xFF;
                            imageData[j+1] = (pixel >> 8) & 0xFF;
                            imageData[j+2] = (pixel >> 16) & 0xFF;
                            prevBuffer[i] = pixel;
                        }
                    }

                    this.canvasContext.putImageData(this.canvasImageData, 0, 0);
                }
            };
        
            return UI;
        };
    })(jQuery);
}
